﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Specialized;

namespace SkypeMonitorService
{
	public sealed class NativeMethods
	{
		public const int FO_DELETE = 3;
		public const int FOF_ALLOWUNDO = 0x40;
		public const int FOF_NOCONFIRMATION = 0x10;  //No prompt dial

		public const uint SHERB_NOCONFIRMATION = 0x00000001;
		public const uint SHERB_NOPROGRESSUI = 0x00000002;
		public const uint SHERB_NOSOUND = 0x00000004;
		public const uint DF_ALLOWOTHERACCOUNTHOOK = 0x0001;

		#region Enums
		[Flags]
		internal enum ACCESS_MASK : uint
		{
			DELETE = 0x00010000,
			READ_CONTROL = 0x00020000,
			WRITE_DAC = 0x00040000,
			WRITE_OWNER = 0x00080000,
			SYNCHRONIZE = 0x00100000,

			STANDARD_RIGHTS_REQUIRED = 0x000f0000,

			STANDARD_RIGHTS_READ = 0x00020000,
			STANDARD_RIGHTS_WRITE = 0x00020000,
			STANDARD_RIGHTS_EXECUTE = 0x00020000,

			STANDARD_RIGHTS_ALL = 0x001f0000,

			SPECIFIC_RIGHTS_ALL = 0x0000ffff,

			ACCESS_SYSTEM_SECURITY = 0x01000000,

			MAXIMUM_ALLOWED = 0x02000000,

			GENERIC_READ = 0x80000000,
			GENERIC_WRITE = 0x40000000,
			GENERIC_EXECUTE = 0x20000000,
			GENERIC_ALL = 0x10000000,

			DESKTOP_READOBJECTS = 0x00000001,
			DESKTOP_CREATEWINDOW = 0x00000002,
			DESKTOP_CREATEMENU = 0x00000004,
			DESKTOP_HOOKCONTROL = 0x00000008,
			DESKTOP_JOURNALRECORD = 0x00000010,
			DESKTOP_JOURNALPLAYBACK = 0x00000020,
			DESKTOP_ENUMERATE = 0x00000040,
			DESKTOP_WRITEOBJECTS = 0x00000080,
			DESKTOP_SWITCHDESKTOP = 0x00000100,

			WINSTA_ENUMDESKTOPS = 0x00000001,
			WINSTA_READATTRIBUTES = 0x00000002,
			WINSTA_ACCESSCLIPBOARD = 0x00000004,
			WINSTA_CREATEDESKTOP = 0x00000008,
			WINSTA_WRITEATTRIBUTES = 0x00000010,
			WINSTA_ACCESSGLOBALATOMS = 0x00000020,
			WINSTA_EXITWINDOWS = 0x00000040,
			WINSTA_ENUMERATE = 0x00000100,
			WINSTA_READSCREEN = 0x00000200,

			WINSTA_ALL_ACCESS = 0x0000037f
		}
		#endregion

		#region Struct
		[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
		public struct SHQUERYRBINFO
		{
			public int cbSize;
			public long i64Size;
			public long i64NumItems;
		}

		[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet = System.Runtime.InteropServices.CharSet.Auto, Pack = 1)]
		public struct SHFILEOPSTRUCT
		{
			public IntPtr hwnd;
			[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.U4)]
			public int wFunc;
			public string pFrom;
			public string pTo;
			public short fFlags;
			[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
			public bool fAnyOperationsAborted;
			public IntPtr hNameMappings;
			public string lpszProgressTitle;

		}
		#endregion

		[System.Runtime.InteropServices.DllImport("shell32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true)]
		internal static extern bool SHEmptyRecycleBin(IntPtr hwnd, string pszRootPath, uint dwSHERB);

		[System.Runtime.InteropServices.DllImport("shell32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true)]
		internal static extern int SHUpdateRecycleBinIcon();

		[System.Runtime.InteropServices.DllImport("shell32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true)]
		internal static extern bool IsUserAnAdmin();

		[System.Runtime.InteropServices.DllImport("shell32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true)]
		internal static extern int SHQueryRecycleBin(string pszRootPath, ref SHQUERYRBINFO pSHQueryRBInfo);

		[System.Runtime.InteropServices.DllImport("shell32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true)]
		internal static extern int SHFileOperation(ref SHFILEOPSTRUCT FileOp);

		// For Windows Mobile, replace user32.dll with coredll.dll
		[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
		internal static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

		[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
		[return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
		internal static extern bool SetForegroundWindow(IntPtr hWnd);

		[System.Runtime.InteropServices.DllImport("User32.dll", SetLastError = true)]
		internal static extern IntPtr GetProcessWindowStation();

		[System.Runtime.InteropServices.DllImport("User32.dll", SetLastError = true)]
		[return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
		internal static extern bool GetUserObjectInformation(IntPtr hObj, int nIndex, out IntPtr pvInfo, uint nLength, ref int lpnLengthNeeded);

		[System.Runtime.InteropServices.DllImport("User32.dll", SetLastError = true)]
		[return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
		internal static extern bool SetProcessWindowStation(IntPtr hWinSta);

		[System.Runtime.InteropServices.DllImport("User32.dll", SetLastError = true)]
		[return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
		internal static extern bool EnumDesktops(IntPtr hwinsta, EnumDesktopProc lpEnumFunc, IntPtr lParam);

		internal delegate bool EnumDesktopProc(string lpszDesktop, IntPtr lParam);
		//[System.Runtime.InteropServices.DllImport("User32.dll", SetLastError = true)]
		//[return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
		//internal static extern bool EnumDesktopProc(  string lpszDesktop,IntPtr lParam);

		[System.Runtime.InteropServices.DllImport("User32.dll", SetLastError = true)]
		[return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
		internal static extern bool SwitchDesktop(IntPtr hDesktop);

		[System.Runtime.InteropServices.DllImport("user32.dll")]
		internal static extern bool CloseDesktop(IntPtr hDesktop);

		[System.Runtime.InteropServices.DllImport("User32.dll", SetLastError = true)]
		internal static extern IntPtr OpenDesktop(string lpszDesktop, uint dwFlags, bool fInherit, ACCESS_MASK dwDesiredAccess);

		[System.Runtime.InteropServices.DllImport("User32.dll", SetLastError = true)]
		internal static extern IntPtr OpenInputDesktop(uint dwFlags, bool fInherit, ACCESS_MASK dwDesiredAccess);

		[System.Runtime.InteropServices.DllImport("kernel32.dll")]
		internal static extern int GetThreadId(IntPtr thread);

		[System.Runtime.InteropServices.DllImport("kernel32.dll")]
		internal static extern int GetProcessId(IntPtr process);

		[System.Runtime.InteropServices.DllImport("advapi32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true)]
		[return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
		internal static extern bool DuplicateToken(IntPtr hToken, int impersonationLevel, out IntPtr hNewToken);

		public const UInt32 STANDARD_RIGHTS_REQUIRED = 0x000F0000;
		public const UInt32 STANDARD_RIGHTS_READ = 0x00020000;
		public const UInt32 TOKEN_ASSIGN_PRIMARY = 0x0001;
		public const UInt32 TOKEN_DUPLICATE = 0x0002;
		public const UInt32 TOKEN_IMPERSONATE = 0x0004;
		public const UInt32 TOKEN_QUERY = 0x0008;
		public const UInt32 TOKEN_QUERY_SOURCE = 0x0010;
		public const UInt32 TOKEN_ADJUST_PRIVILEGES = 0x0020;
		public const UInt32 TOKEN_ADJUST_GROUPS = 0x0040;
		public const UInt32 TOKEN_ADJUST_DEFAULT = 0x0080;
		public const UInt32 TOKEN_ADJUST_SESSIONID = 0x0100;
		public const UInt32 TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY);
		public const UInt32 TOKEN_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY |
			TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE |
			TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT |
			TOKEN_ADJUST_SESSIONID);

		[System.Runtime.InteropServices.DllImport("advapi32.dll", SetLastError = true)]
		[return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
		internal static extern bool OpenProcessToken(IntPtr ProcessHandle,UInt32 DesiredAccess, out IntPtr TokenHandle);

		[System.Runtime.InteropServices.DllImport("advapi32.dll", SetLastError = true)]
		[return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
		internal static extern bool ImpersonateLoggedOnUser(IntPtr hToken);

		[System.Runtime.InteropServices.DllImport("advapi32.dll", SetLastError = true)]
		[return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
		internal static extern bool RevertToSelf();


		#region Key handle (tracking)
		// Crownwood.DotNetMagic.Win32.User32
		[System.Runtime.InteropServices.DllImport("User32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
		internal static extern ushort GetKeyState(int virtKey);

		internal enum VirtualKeys
		{
			VK_LBUTTON = 1,
			VK_CANCEL = 3,
			VK_BACK = 8,
			VK_TAB,
			VK_CLEAR = 12,
			VK_RETURN,
			VK_SHIFT = 16,
			VK_CONTROL,
			VK_MENU,
			VK_CAPITAL = 20,
			VK_ESCAPE = 27,
			VK_SPACE = 32,
			VK_PRIOR,
			VK_NEXT,
			VK_END,
			VK_HOME,
			VK_LEFT,
			VK_UP,
			VK_RIGHT,
			VK_DOWN,
			VK_SELECT,
			VK_EXECUTE = 43,
			VK_SNAPSHOT,
			VK_HELP = 47,
			VK_0,
			VK_1,
			VK_2,
			VK_3,
			VK_4,
			VK_5,
			VK_6,
			VK_7,
			VK_8,
			VK_9,
			VK_A = 65,
			VK_B,
			VK_C,
			VK_D,
			VK_E,
			VK_F,
			VK_G,
			VK_H,
			VK_I,
			VK_J,
			VK_K,
			VK_L,
			VK_M,
			VK_N,
			VK_O,
			VK_P,
			VK_Q,
			VK_R,
			VK_S,
			VK_T,
			VK_U,
			VK_V,
			VK_W,
			VK_X,
			VK_Y,
			VK_Z,
			VK_NUMPAD0 = 96,
			VK_NUMPAD1,
			VK_NUMPAD2,
			VK_NUMPAD3,
			VK_NUMPAD4,
			VK_NUMPAD5,
			VK_NUMPAD6,
			VK_NUMPAD7,
			VK_NUMPAD8,
			VK_NUMPAD9,
			VK_MULTIPLY,
			VK_ADD,
			VK_SEPARATOR,
			VK_SUBTRACT,
			VK_DECIMAL,
			VK_DIVIDE,
			VK_F10 = 121,
			VK_ATTN = 246,
			VK_CRSEL,
			VK_EXSEL,
			VK_EREOF,
			VK_PLAY,
			VK_ZOOM,
			VK_NONAME,
			VK_PA1,
			VK_OEM_CLEAR,
			VK_LWIN = 91,
			VK_RWIN,
			VK_APPS,
			VK_LSHIFT = 160,
			VK_RSHIFT,
			VK_LCONTROL,
			VK_RCONTROL,
			VK_LMENU,
			VK_RMENU
		}
		#endregion

		#region Hotkeys api
		[System.Runtime.InteropServices.DllImport("kernel32.dll")]
		public static extern int GlobalAddAtom(string lpString);
		[System.Runtime.InteropServices.DllImport("kernel32.dll")]
		public static extern int GlobalDeleteAtom(int nAtom);

		[System.Runtime.InteropServices.DllImport("User32")]
		public static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vk);

		[System.Runtime.InteropServices.DllImport("User32")]
		public static extern bool UnregisterHotKey(IntPtr hWnd, int id);

		[System.Runtime.InteropServices.DllImport("User32")]
		public static extern IntPtr GetForegroundWindow();

		[System.Runtime.InteropServices.DllImport("user32")]
		public static extern int SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);

		public const int MOD_SHIFT = 0x4;
		public const int MOD_CONTROL = 0x2;
		public const int MOD_ALT = 0x1;
		public const int MOD_WIN = 0x8;
		public const int WM_HOTKEY = 0x312;
		public const int WM_MOUSEWHEEL = 0x020A;
		public const int WM_PASTE = 0x0302;
		#endregion

		#region Shell32 dll
		internal enum ShowCommands : int
		{
			SW_HIDE = 0,
			SW_SHOWNORMAL = 1,
			SW_NORMAL = 1,
			SW_SHOWMINIMIZED = 2,
			SW_SHOWMAXIMIZED = 3,
			SW_MAXIMIZE = 3,
			SW_SHOWNOACTIVATE = 4,
			SW_SHOW = 5,
			SW_MINIMIZE = 6,
			SW_SHOWMINNOACTIVE = 7,
			SW_SHOWNA = 8,
			SW_RESTORE = 9,
			SW_SHOWDEFAULT = 10,
			SW_FORCEMINIMIZE = 11,
			SW_MAX = 11
		}

		[System.Runtime.InteropServices.DllImport("shell32.dll")]
		internal static extern IntPtr ShellExecute(IntPtr hwnd,
			string lpOperation,
			string lpFile,
			string lpParameters,
			string lpDirectory,
			ShowCommands nShowCmd);
		#endregion
	}

	public static class UIHelper
	{
		private static StringCollection m_sc = null;
		/// <summary>
		/// Bring window to top of most program
		/// </summary>
		/// <param name="windowName"></param>
		/// <param name="wait"></param>
		/// <returns></returns>
		public static bool BringWindowToTop(string windowName, bool wait)
		{
			IntPtr hWnd = NativeMethods.FindWindow(null, windowName);
			if (hWnd != IntPtr.Zero)
			{
				return NativeMethods.SetForegroundWindow(hWnd);
			}
			return false;
		}

		/// <summary>
		/// Enumerates all desktops associated with the specified window station of the calling process
		/// </summary>
		/// <returns></returns>
		public static string[] GetDesktops()
		{
			// attempt to enum desktops.
			IntPtr windowStation = NativeMethods.GetProcessWindowStation();

			// check we got a valid handle.
			if (windowStation == IntPtr.Zero) return new string[0];

			string[] desktops;

			// lock the object. thread safety and all.
			lock (m_sc = new StringCollection())
			{
				bool result = NativeMethods.EnumDesktops(windowStation, new NativeMethods.EnumDesktopProc(DesktopProc), IntPtr.Zero);

				// something went wrong.
				if (!result) return new string[0];

				//	// turn the collection into an array.
				desktops = new string[m_sc.Count];
				for (int i = 0; i < desktops.Length; i++)
					desktops[i] = m_sc[i];
			}

			return desktops;
		}

		private static bool DesktopProc(string lpszDesktop, IntPtr lParam)
		{
			// add the desktop to the collection.
			m_sc.Add(lpszDesktop);

			return true;
		}


	}

	public static class RecycleBinHelper
	{
		/// <summary>
		/// Get numbers of items in RecycleBin
		/// </summary>
		/// <exception cref="System.ComponentModel.Win32Exception"/>
		/// <returns>number of counts</returns>
		public static long GetItemsCount()
		{
			NativeMethods.SHQUERYRBINFO info = new NativeMethods.SHQUERYRBINFO();
			info.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(NativeMethods.SHQUERYRBINFO));
			int rez = NativeMethods.SHQueryRecycleBin("", ref info);
			if (rez != 0)
				throw new System.ComponentModel.Win32Exception(System.Runtime.InteropServices.Marshal.GetLastWin32Error());

			return info.i64NumItems;
		}


		/// <summary>
		/// Delete file to recyle bin
		/// </summary>
		/// <param name="fPath">Path to file</param>
		/// <exception cref="System.ArgumentException"/>
		/// <exception cref="System.IO.FileNotFoundException"/>
		/// <exception cref="System.ComponentModel.Win32Exception"/>
		public static void DeleteFileToRecycleBin(string fPath)
		{
			if (string.IsNullOrEmpty(fPath))
				throw new ArgumentException("fPath");
			if (!System.IO.File.Exists(fPath))
				throw new System.IO.FileNotFoundException("File not found", fPath);

			NativeMethods.SHFILEOPSTRUCT shf = new NativeMethods.SHFILEOPSTRUCT();
			shf.wFunc = NativeMethods.FO_DELETE;
			shf.fFlags = NativeMethods.FOF_ALLOWUNDO | NativeMethods.FOF_NOCONFIRMATION;
			shf.pFrom = fPath;
			int result = NativeMethods.SHFileOperation(ref shf);
			if (result != 0)
				throw new System.ComponentModel.Win32Exception(System.Runtime.InteropServices.Marshal.GetLastWin32Error());
		}

		/// <summary>
		/// Delete file to recyle bin
		/// </summary>
		/// <param name="fPath">Path to file</param>
		/// <exception cref="System.ArgumentException"/>
		/// <exception cref="System.IO.FileNotFoundException"/>
		/// <exception cref="System.ComponentModel.Win32Exception"/>
		public static void DeleteFileToRecycleBinWithConfirmation(string fPath)
		{
			if (string.IsNullOrEmpty(fPath))
				throw new ArgumentException("fPath");
			if (!System.IO.File.Exists(fPath))
				throw new System.IO.FileNotFoundException("File not found", fPath);

			NativeMethods.SHFILEOPSTRUCT shf = new NativeMethods.SHFILEOPSTRUCT();
			shf.wFunc = NativeMethods.FO_DELETE;
			shf.fFlags = NativeMethods.FOF_ALLOWUNDO;
			shf.pFrom = fPath;
			int result = NativeMethods.SHFileOperation(ref shf);
			if (result != 0)
				throw new System.ComponentModel.Win32Exception(System.Runtime.InteropServices.Marshal.GetLastWin32Error());
		}

		/// <summary>
		/// Delete file list to recyle bin
		/// </summary>
		/// <param name="fPath">Path to file</param>
		/// <exception cref="System.ArgumentException"/>
		/// <exception cref="System.ComponentModel.Win32Exception"/>
		public static bool DeleteFiles(IEnumerable<string> files, bool toRecycleBin)
		{
			if (files == null)
				throw new ArgumentException("files");

			NativeMethods.SHFILEOPSTRUCT shf = new NativeMethods.SHFILEOPSTRUCT();
			shf.wFunc = NativeMethods.FO_DELETE;
			shf.fFlags = (short)(toRecycleBin ? NativeMethods.FOF_ALLOWUNDO : 0);

			StringBuilder fls = new StringBuilder();
			foreach (string f in files)
			{
				fls.AppendFormat("{0}\0", f);
			}
			shf.pFrom = fls.ToString();

			int hResult = NativeMethods.SHFileOperation(ref shf);

			if (hResult != 0) throw new System.ComponentModel.Win32Exception(System.Runtime.InteropServices.Marshal.GetLastWin32Error());

			return !shf.fAnyOperationsAborted;
		}
	}
}
